(ns
    ^{:doc "Handy fn generators"
      :author "Sam Aaron"}
  overtone.algo.fn)

(defn cycle-fn
  "Returns a fn which will cycle between each of the fns specified. The
   first time the fn is called, the first fn in fns will be called, the
   second time, the second fn in fns etc, cycling around indefinitely.

   The args passed into the returned fn will be passed through to the
   appropriate inner fn. Additionally, the result of the inner fn will
   be returned as the result of the outer fn.

   Will catch any exceptions generated by the internal fns and print a
   stacktrace to stdout.

   (def f (cycle-fn (fn [st] (println \"hi\" st))
                    (fn [st] (println \"yo\" st))
                    (fn [st] (println \"ho ho\" st))))

   (f \"there\") ;=> \"hi there\"
   (f \"bro\")   ;=> \"yo bro\"
   (f \"santa\") ;=> \"ho ho santa\"
   (f \"again\") ;=> \"hi again\""
  [& fns]
  (let [stored-fns (agent (cycle fns))]
    (fn [& args]
      (let [res-prom (promise)]
        (send-off stored-fns
                  (fn [v]
                    (try
                      (let [res (apply (first v) args)]
                        (deliver res-prom res))
                      (catch Exception e
                        (.printStackTrace e)
                        (deliver res-prom nil)))
                    (rest v)))
        @res-prom))))
